package cool.lichuan.zabbix.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

/**
 * @author lichuan
 * @date 2021/11/19 16:26
 * @description 参数类型控制
 */
public class ParamBuilder<T> implements Serializable {
    private static final long serialVersionUID = -7164479980721034714L;
    private static final Logger log = LoggerFactory.getLogger(ParamBuilder.class);
    private final HashMap<String, Object> params = new HashMap<>(16);
    private final HashMap<String, Object> filterParams = new HashMap<>(16);
    private final HashMap<String, Object> searchParams = new HashMap<>(16);
    private final ArrayList<String> sortFieldList = new ArrayList<>();
    private final ArrayList<String> sortOrderList = new ArrayList<>();

    public static <T>ParamBuilder<T> builder() {
        return new ParamBuilder<>();
    }

    public static HashMap<String, Object> oneParam(String key, Object value) {
        return builder().put(key, value).build();
    }

    public HashMap<String, Object> build() {
        return params;
    }

    /**
     * 参数传入
     *
     * @param key   参数名
     * @param value 参数值
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> put(String key, Object value) {
        if (StringUtils.hasLength(key) && null != value) {
            params.put(key, value);
        }
        return this;
    }

    /**
     * 参数传入
     *
     * @param fn    通过方法引用的方式传入字段名 比如 HostResp::getHostId
     * @param value 传入参数值
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> put(FieldUtil.FieldFunction<T, ?> fn, Object value) {
        return put(FieldUtil.getName(fn), value);
    }

    // --------------------------- 常用条件 开始 --------------------------------

    /**
     * 要返回的对象属性, 返回对象的字段: 取值范围：extend(所有字段，默认值)、数组(自定义返回字段) 比如：["hostid", "host"]
     *
     * @param arr 返回字段名
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> output(String[] arr) {
        if (ArrUtil.isNotEmpty(arr)) {
            return put("output", arr);
        }
        return this;
    }

    /**
     * 仅返回与给定过滤条件完全匹配的结果。
     * 过滤条件是一个数组，其中键是属性名，值可以是单个值或要匹配的值数组。
     * 不适用于 text 字段。 比如 "port": ["10050", "10071"]
     *
     * @param name 过滤字段
     * @param arr  过滤值
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> filter(String name, Object[] arr) {
        if (StringUtils.hasLength(name) && ArrUtil.isNotEmpty(arr)) {
            // 此处不做重复键校验，重复覆盖
            filterParams.put(name, arr);
            return put("filter", filterParams);
        }
        return this;
    }

    /**
     * 返回结果中的记录数，而不是实际的数据。
     *
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> countOutput() {
        return put("countOutput", true);
    }

    /**
     * 返回给定通配符（不区分大小写）匹配到的结果。
     * 接受一个数组，键是属性名，其值是要搜索的字符串。如果没有其他选项，将执行LIKE "%…%"搜索 。 比如："host": ["MySQL", "Linux"]
     *
     * @param name 字段名
     * @param arr  搜索条件
     * @return @return 返回map，可继续构造
     */
    public ParamBuilder<T> search(String name, Object[] arr) {
        if (StringUtils.hasLength(name) && ArrUtil.isNotEmpty(arr)) {
            // 此处不做重复键校验，重复覆盖
            searchParams.put(name, arr);
            return put("search", searchParams);
        }
        return this;
    }
    // --------------------------- 常用条件 结束 --------------------------------

    // --------------------------- 不常用条件 开始 ------------------------------

    /**
     * 如果设置为 true ，则只返回用户具有写权限的对象 默认值: false。
     *
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> editable() {
        return put("editable", true);
    }

    /**
     * 返回与在 search 参数中给定的条件不匹配的结果
     *
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> excludeSearch() {
        return put("excludeSearch", true);
    }

    /**
     * 限制返回记录的数据
     *
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> limit(int limit) {
        return put("limit", limit);
    }

    /**
     * 在结果数组中，使用 ID 作为键。
     *
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> preserveKeys() {
        return put("preservekeys", true);
    }

    /**
     * 如果设置为 true，则返回在 filter 或 search 参数中给出的任何条件匹配的结果，而不是所有条件。
     *
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> searchByAny() {
        return put("searchByAny", true);
    }

    /**
     * 如果设置为 true，则可以在search 中使用 "*" 作为通配符。
     *
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> searchWildcardsEnabled() {
        return put("searchWildcardsEnabled", true);
    }

    /**
     * 按给定属性对结果进行排序。有关可用于排序的属性列表，请参考特定的API get方法描述。宏在排序前不会被展开。
     * @param value 排序字段， 可为String/Array 比如："sortfield": "host"
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> sortField(Object value) {
        if (value instanceof String){
            sortFieldList.add((String) value);
        } if(value instanceof Array) {
            String[] valueStr = (String[]) value;
            sortFieldList.addAll(Arrays.asList(valueStr));
        } else {
            log.error("排序字段仅支持String/Array类型");
            return this;
        }
        return put("sortfield", sortFieldList.toArray());
    }

    /**
     * 排序顺序。 如果传递数组，则每个值都将与 sortfield 参数中给定的相应属性匹配。
     *
     * 可选的值为:
     * ASC - 升序;
     * DESC - 降序。
     * @param value 排序类型
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> sortOrder(Object value) {
        if (value instanceof String){
            sortOrderList.add((String) value);
        } if(value instanceof Array) {
            String[] valueStr = (String[]) value;
            sortOrderList.addAll(Arrays.asList(valueStr));
        } else {
            log.error("排序方式仅支持String/Array类型");
            return this;
        }
        return put("sortorder", sortOrderList.toArray());
    }

    /**
     * 	search 参数将比较字段的开始，即执行 LIKE "…%" 搜索。
     *
     * 如果 searchWildcardsEnabled 设置为 true，则忽略
     *
     * @return 返回map，可继续构造
     */
    public ParamBuilder<T> startSearch() {
        return put("startSearch", true);
    }

    // --------------------------- 不常用条件 结束 ------------------------------
}
